package com.gitee.wsl.mathematics.vector


import com.gitee.wsl.ext.base.compare
import com.gitee.wsl.mathematics.number.DoubleCalcContainer
import com.gitee.wsl.mathematics.number.FloatCalcContainer
import com.gitee.wsl.mathematics.number.compareTo


interface Vector<T:Number,V: Vector<T, V>>: VectorCalcContainer<T,V>,Comparable<V> {

    //override fun create(values: List<T>):V

    /**
     * Dimension
     * 维数
     */
    val dim: Int get() = proxy.size

    val squaredLength:T

    operator fun times(other: V) :V = create(proxy.mapIndexed{ index, i ->  i * other.proxy[index] })
    operator fun div(other: V) :V = create(proxy.mapIndexed{ index, i ->  i / other.proxy[index] })
    operator fun rem(other: V) :V = create(proxy.mapIndexed{ index, i ->  i % other.proxy[index] })

    val length:T
        get() = sqrt(squaredLength)

    val normalized:V get() = this / length

    /**
     * 判断是否单位向量
     */
    val isNormalized get()  = length == one

    val isUnit get() = abs(squaredLength - 1f).isApproxZero()

    val unit: V
        get() {
            val norm = length
            return if (norm.isApproxZero()) self else normalized
        }

    override fun compareTo(other: V): Int = this.proxy.values().compare(other.proxy.values()){a,b-> a.minOf { it.toDouble() }.compareTo(b.minOf { it.toDouble() })}

    fun limit(limit: T): V = limit2(limit * limit)

    fun limit2(limit2: T): V {
        val len2 = squaredLength
        return if (len2 > limit2) {
            times(sqrt(limit2 / len2))
        } else self
    }

    /*val abs:V
        get() = create(proxy.map{ i ->  abs(i)  })
    val sign:V
        get() = create(proxy.map{ i ->  sign(i)  })
    val ceil:V
        get() = create(proxy.map{ i ->  ceil(i)  })
    val floor:V
        get() = create(proxy.map{ i ->  floor(i)  })
    val round:V
        get() = create(proxy.map{ i ->  round(i)  })*/


   /* operator fun plus(other: V) :V = create(proxy.mapIndexed{ index, i ->  i + other.proxy[index] })

    operator fun minus(other: V) :V = create(proxy.mapIndexed{ index, i ->  i - other.proxy[index] })

    operator fun times(a: Number) :V = create( proxy.map{ i ->  i * a })

    operator fun div(a: Number):V = create( proxy.map{ i ->  i / a })

    fun divBy(a: Number) :V = create( proxy.map{ i ->  i.divBy(a) })

    operator fun rem(a: Number):V = create( proxy.map{ i ->  i.rem(a) })
    //operator fun rem(other: V) :V //= create((0..proxy.size).mapIndexed{ index, i ->  i ^ other.proxy[index] })
    fun remBy(a: Number) :V =create( proxy.map{  i ->  i.remBy(a) })

    operator fun unaryMinus() : V = times(-1)*/

    /**The normal direction from this vector to [other].*/
    //infix fun directionTo(other: V) : V

    /**The distance from this vector to [other].*/
    //infix fun distanceTo(other: V):T

    /**Reflects the vector from [normal].*/
    //infix fun reflectFrom(normal: V): V

    /**The projection of this vector onto [other].*/
    //infix fun projectOn(other: V) :V

    /**
     * Dot product.
     * @return The dot product between this vector and [other].
     */
    //infix fun dot(other: V) : T


}

/*
private inline operator fun<reified T:Number,reified V:Vector<T,*>,reified R:Vector<T,*>> V.plus(other: R) = if(this.valueArray.size < other.valueArray.size) other._create(other.valueArray.zip(valueArray){a,b->a+b}.toTypedArray()) else _create ( valueArray + other.valueArray )

private inline operator fun<reified T:Number> List<T>.plus(other:List<T>):List<T>{
    val ret = ArrayList<T>(size)
    forEachIndexed { index, d ->
        ret[index] = d + other[index]
    }
    return ret
}

operator fun<R: Vector<Double, *>>  minus(other: R) :V

operator fun<R: Vector<Double, *>>  times(other: R) :V

operator fun<R: Vector<Double, *>>  div(other: R) :V

operator fun<R: Vector<Double, *>>  rem(other: R) :V*/

interface VectorD<V: Vector<Double, V>>: Vector<Double, V>, DoubleCalcContainer

interface VectorF<V: Vector<Float, V>>: Vector<Float, V>, FloatCalcContainer
